home *** CD-ROM | disk | FTP | other *** search
/ InterCD 2001 May / may_2001.iso / intercd / root / Multimedia / ^DivX_Article / virtualdub / VirtualDub-source-1_4d / HexViewer.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  2001-03-28  |  42.6 KB  |  1,793 lines

  1. //    VirtualDub - Video processing and capture application
  2. //    Copyright (C) 1998-2001 Avery Lee
  3. //
  4. //    This program is free software; you can redistribute it and/or modify
  5. //    it under the terms of the GNU General Public License as published by
  6. //    the Free Software Foundation; either version 2 of the License, or
  7. //    (at your option) any later version.
  8. //
  9. //    This program is distributed in the hope that it will be useful,
  10. //    but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. //    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12. //    GNU General Public License for more details.
  13. //
  14. //    You should have received a copy of the GNU General Public License
  15. //    along with this program; if not, write to the Free Software
  16. //    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  17.  
  18. #define f_HEXVIEWER_CPP
  19.  
  20. #define _WIN32_WINNT 0x0500
  21.  
  22. #include <stdio.h>
  23. #include <ctype.h>
  24. #include <crtdbg.h>
  25.  
  26. #include <windows.h>
  27. #include <commctrl.h>
  28. #include <commdlg.h>
  29.  
  30. #include "resource.h"
  31. #include "oshelper.h"
  32. #include "gui.h"
  33. #include "error.h"
  34. #include "list.h"
  35.  
  36. #include "HexViewer.h"
  37. #include "ProgressDialog.h"
  38.  
  39. extern HINSTANCE g_hInst;
  40.  
  41. ////////////////////////////
  42.  
  43. extern const char szHexViewerClassName[]="birdyHexEditor";
  44. static const char g_szHexWarning[]="Hex editor warning";
  45.  
  46. ////////////////////////////
  47.  
  48. class HVModifiedLine : public ListNode2<HVModifiedLine> {
  49. public:
  50.     char            data[16];
  51.     __int64            address;
  52.     int                mod_flags;
  53. private:
  54.  
  55. public:
  56.     HVModifiedLine(__int64 addr);
  57. };
  58.  
  59. HVModifiedLine::HVModifiedLine(__int64 addr)
  60.     : address(addr)
  61.     , mod_flags(0)
  62. {}
  63.  
  64.  
  65.  
  66. ////////////////////////////
  67.  
  68. class HexViewer {
  69. private:
  70.     const HWND    hwnd;
  71.     HWND    hwndFind;
  72.     HWND    hwndTree;
  73.     HANDLE    hFile;
  74.     HFONT    hfont;
  75.     __int64    i64TopOffset;
  76.     __int64 i64FileSize;
  77.     __int64    i64Position;
  78.     int        nCharWidth;
  79.     int        nLineHeight;
  80.     int        nLineLimit;
  81.     int        nCurrentVisLines;
  82.     int        nCurrentWholeLines;
  83.     int        iMouseWheelDelta;
  84.  
  85.     char    rowcache[16];
  86.     __int64    i64RowCacheAddr;
  87.  
  88.     List2<HVModifiedLine>    listMods;
  89.  
  90.     ModelessDlgNode    mdnFind;
  91.     char    *pszFindString;
  92.     int        nFindLength;
  93.     bool    bFindCaseInsensitive;
  94.     bool    bFindHex;
  95.     bool    bFindReverse;
  96.  
  97.     bool    bCharMode;
  98.     bool    bOddHex;
  99.     bool    bEnableWrite;
  100.     bool    bCaretHidden;
  101.  
  102. public:
  103.     HexViewer(HWND);
  104.     ~HexViewer();
  105.  
  106.     static LRESULT APIENTRY HexViewerWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) throw();
  107.     static BOOL APIENTRY FindDlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) throw();
  108.     static BOOL APIENTRY TreeDlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) throw();
  109.  
  110. private:
  111.     void Init() throw();
  112.     void Open() throw();
  113.     void Open(const char *pszFile, bool bRW) throw();
  114.     void Close() throw();
  115.     void Commit() throw();
  116.  
  117.     const char *FillRowCache(__int64 line) throw();
  118.     void InvalidateLine(__int64 line) throw();
  119.     void ScrollTopTo(long lLine) throw();
  120.     void ScrollVisible(__int64 nVisPos) throw();
  121.     void MoveCaret() throw();
  122.     void MoveCaretToByte(__int64 pos) throw();
  123.  
  124.     LRESULT Handle_WM_COMMAND(WPARAM wParam, LPARAM lParam) throw();
  125.     LRESULT Handle_WM_PAINT(WPARAM wParam, LPARAM lParam) throw();
  126.     LRESULT Handle_WM_VSCROLL(WPARAM wParam, LPARAM lParam) throw();
  127.     LRESULT Handle_WM_SIZE(WPARAM wParam, LPARAM lParam) throw();
  128.     LRESULT Handle_WM_MOUSEWHEEL(WPARAM wParam, LPARAM lParam) throw();
  129.     LRESULT Handle_WM_KEYDOWN(WPARAM wParam, LPARAM lParam) throw();
  130.     LRESULT Handle_WM_LBUTTONDOWN(WPARAM wParam, LPARAM lParam) throw();
  131.     LRESULT Handle_WM_CHAR(WPARAM wParam, LPARAM lParam) throw();
  132.  
  133.     static BOOL CALLBACK AskForValuesDlgProc(HWND hdlg, UINT msg, WPARAM wParam, LPARAM lParam) throw();
  134.     bool AskForValues(const char *title, const char *name1, const char *name2, __int64& default1, __int64& default2, int (HexViewer::*verifier)(HWND hdlg, __int64 v1, __int64 v2) throw()) throw();
  135.     int JumpVerifier(HWND hdlg, __int64 v1, __int64 v2) throw();
  136.     int ExtractVerifier(HWND hdlg, __int64 v1, __int64 v2) throw();
  137.     int TruncateVerifier(HWND hdlg, __int64 v1, __int64 v2) throw();
  138.  
  139.     void Extract() throw();
  140.     void Find() throw();
  141.     void _RIFFScan(struct RIFFScanInfo &rsi, HWND hwndTV, HTREEITEM hti, __int64 pos, __int64 sizeleft);
  142.     void RIFFTree(HWND hwndTV) throw();
  143.  
  144.     HVModifiedLine *FindModLine(__int64 addr) throw() {
  145.         HVModifiedLine *pLine, *pLineNext;
  146.  
  147.         pLine = listMods.AtHead();
  148.  
  149.         while(pLineNext = pLine->NextFromHead()) {
  150.             if (addr == pLine->address)
  151.                 return pLine;
  152.  
  153.             pLine = pLineNext;
  154.         }
  155.  
  156.         return NULL;
  157.     }
  158.  
  159.     void Hide() throw() {
  160.         if (!bCaretHidden) {
  161.             bCaretHidden = true;
  162.             HideCaret(hwnd);
  163.         }
  164.     }
  165.  
  166.     void Show() throw() {
  167.         if (bCaretHidden) {
  168.             bCaretHidden = false;
  169.             ShowCaret(hwnd);
  170.         }
  171.     }
  172. };
  173.  
  174. ////////////////////////////
  175.  
  176. HexViewer::HexViewer(HWND _hwnd) : hwnd(_hwnd), hwndFind(0), hwndTree(0), pszFindString(NULL), bFindReverse(false), hfont(0) {
  177.     hFile = INVALID_HANDLE_VALUE;
  178.  
  179.     i64TopOffset = i64FileSize = 0;
  180.     iMouseWheelDelta = 0;
  181.     i64Position = 0;
  182.     bCharMode = false;
  183.     bOddHex = false;
  184.     i64RowCacheAddr = -1;
  185. }
  186.  
  187. HexViewer::~HexViewer() {
  188.     delete[] pszFindString;
  189.     pszFindString = NULL;
  190.  
  191.     Close();
  192.  
  193.     if (hwndTree)
  194.         DestroyWindow(hwndTree);
  195.  
  196.     if (hwndFind)
  197.         DestroyWindow(hwndFind);
  198.  
  199.     if (hfont)
  200.         DeleteObject(hfont);
  201. }
  202.  
  203. void HexViewer::Init() {
  204.     HDC hdc;
  205.  
  206.     nLineHeight = 16;
  207.     if (hdc = GetDC(hwnd)) {
  208.         TEXTMETRIC tm;
  209.         HGDIOBJ hfOld;
  210.  
  211.         hfOld = SelectObject(hdc, GetStockObject(ANSI_FIXED_FONT));
  212.  
  213.         GetTextMetrics(hdc, &tm);
  214.  
  215.         hfont = CreateFont(tm.tmHeight, 0, 0, 0, 0, FALSE, FALSE, FALSE, ANSI_CHARSET,
  216.             OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, FIXED_PITCH|FF_DONTCARE, "Lucida Console");
  217.  
  218.         SelectObject(hdc, hfont ? hfont : GetStockObject(ANSI_FIXED_FONT));
  219.  
  220.         GetTextMetrics(hdc, &tm);
  221.         nCharWidth    = tm.tmAveCharWidth;
  222.         nLineHeight = tm.tmHeight;
  223.  
  224.         SelectObject(hdc, hfOld);
  225.  
  226.         ReleaseDC(hwnd, hdc);
  227.     }
  228. }
  229.  
  230. void HexViewer::Open() {
  231.     char szName[MAX_PATH];
  232.     OPENFILENAME ofn;
  233.  
  234.     szName[0] = 0;
  235.  
  236.     memset(&ofn, 0, sizeof ofn);
  237.  
  238.     ofn.lStructSize            = 0x4c;    //sizeof(OPENFILENAME); stupid beta include files
  239.     ofn.hwndOwner            = hwnd;
  240.     ofn.lpstrFilter            = "All files (*.*)\0*.*\0";
  241.     ofn.lpstrCustomFilter    = NULL;
  242.     ofn.nFilterIndex        = 1;
  243.     ofn.lpstrFile            = szName;
  244.     ofn.nMaxFile            = sizeof szName;
  245.     ofn.lpstrFileTitle        = NULL;
  246.     ofn.lpstrInitialDir        = NULL;
  247.     ofn.lpstrTitle            = NULL;
  248.     ofn.Flags                = OFN_EXPLORER | OFN_ENABLESIZING | OFN_FILEMUSTEXIST | OFN_READONLY;
  249.     ofn.lpstrDefExt            = NULL;
  250.  
  251.     if (GetOpenFileName(&ofn))
  252.         Open(szName, !(ofn.Flags & OFN_READONLY));
  253. }
  254.  
  255. void HexViewer::Open(const char *pszFile, bool bRW) {
  256.     Close();
  257.  
  258.     bEnableWrite = bRW;
  259.  
  260.     if (bRW)
  261.         hFile = CreateFile(pszFile, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL|FILE_FLAG_SEQUENTIAL_SCAN, NULL);
  262.     else
  263.         hFile = CreateFile(pszFile, GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL|FILE_FLAG_SEQUENTIAL_SCAN, NULL);
  264.  
  265.     if (hFile == INVALID_HANDLE_VALUE) {
  266.         MyWin32Error("Cannot open file: %%s", GetLastError()).post(hwnd, "Hex editor error");
  267.         return;
  268.     }
  269.  
  270.     char buf[512];
  271.  
  272.     wsprintf(buf, "VirtualDub Hex Editor - [%s]%s", pszFile, bRW ? "" : " (read only)");
  273.     SetWindowText(hwnd, buf);
  274.  
  275.     DWORD dwLow, dwHigh;
  276.     
  277.     dwLow = GetFileSize(hFile, &dwHigh);
  278.  
  279.     i64FileSize = dwLow | ((__int64)dwHigh << 32);
  280.  
  281.     SetScrollRange(hwnd, SB_VERT, 0, (int)(i64FileSize>>4), TRUE);
  282.  
  283.     i64RowCacheAddr    = -1;
  284.     i64TopOffset    = 0;
  285.     nLineLimit        = (int)((i64FileSize+15)>>4);
  286.     InvalidateRect(hwnd, NULL, TRUE);
  287.     UpdateWindow(hwnd);
  288. }
  289.  
  290. void HexViewer::Close() {
  291.     HVModifiedLine *pLine;
  292.  
  293.     while(pLine = listMods.RemoveHead())
  294.         delete pLine;
  295.  
  296.     if (hFile == INVALID_HANDLE_VALUE)
  297.         return;
  298.  
  299.     CloseHandle(hFile);
  300.     hFile = INVALID_HANDLE_VALUE;
  301.  
  302.     InvalidateRect(hwnd, NULL, TRUE);
  303.     UpdateWindow(hwnd);
  304.  
  305.     if (hwndTree)
  306.         DestroyWindow(hwndTree);
  307. }
  308.  
  309. void HexViewer::Commit() {
  310.     HVModifiedLine *pLine;
  311.  
  312.     while(pLine = listMods.RemoveHead()) {
  313.         DWORD dwBytes = 16, dwActual;
  314.  
  315.         if (((unsigned __int64)pLine->address>>32) == ((unsigned __int64)i64FileSize >> 32))
  316.             if (!(((long)pLine->address ^ (long)i64FileSize) & 0xfffffff0))
  317.                 dwBytes = (long)i64FileSize - (long)pLine->address;
  318.  
  319.         SetFilePointer(hFile, (LONG)pLine->address, (LONG *)&pLine->address + 1, FILE_BEGIN);
  320.         WriteFile(hFile, pLine->data, dwBytes, &dwActual, NULL);
  321.         delete pLine;
  322.     }
  323.  
  324.     InvalidateRect(hwnd, NULL, TRUE);
  325. }
  326.  
  327. const char *HexViewer::FillRowCache(__int64 i64Offset) throw() {
  328.     if (i64Offset == i64RowCacheAddr)
  329.         return rowcache;
  330.  
  331.     DWORD dwActual;
  332.  
  333.     i64RowCacheAddr = i64Offset;
  334.  
  335.     SetFilePointer(hFile, (LONG)i64Offset, (LONG *)&i64Offset + 1, FILE_BEGIN);
  336.     ReadFile(hFile, rowcache, 16, &dwActual, NULL);
  337.  
  338.     return rowcache;
  339. }
  340.  
  341. void HexViewer::InvalidateLine(__int64 line) throw() {
  342.     int visidx = (int)(line - i64TopOffset) >> 4;
  343.     RECT r;
  344.  
  345.     if (visidx < 0 || visidx >= nCurrentVisLines)
  346.         return;
  347.  
  348.     GetClientRect(hwnd, &r);
  349.     r.top        = nLineHeight * visidx;
  350.     r.bottom    = r.top + nLineHeight;
  351.  
  352.     InvalidateRect(hwnd, &r, TRUE);
  353. }
  354.  
  355. void HexViewer::ScrollTopTo(long lLine) throw() {
  356.     HDC hdc;
  357.     RECT rRedraw;
  358.  
  359.     if (lLine < 0)
  360.         lLine = 0;
  361.  
  362.     if (lLine > nLineLimit)
  363.         lLine = nLineLimit;
  364.  
  365.     long delta = lLine - (long)(i64TopOffset>>4);
  366.  
  367.     if (!delta)
  368.         return;
  369.  
  370.     iMouseWheelDelta = 0;
  371.  
  372.     SetScrollPos(hwnd, SB_VERT, lLine, TRUE);
  373.     i64TopOffset = (__int64)lLine<<4;
  374.  
  375.     Hide();
  376.     if (abs(delta) > nCurrentVisLines) {
  377.         InvalidateRect(hwnd, NULL, TRUE);
  378.     } else {
  379.        if (hdc = GetDC(hwnd)) {
  380.            ScrollDC(hdc, 0, -delta*nLineHeight, NULL, NULL, NULL, &rRedraw);
  381.            ReleaseDC(hwnd, hdc);
  382.            InvalidateRect(hwnd, &rRedraw, TRUE);
  383.            UpdateWindow(hwnd);
  384.        }
  385.     }
  386.     MoveCaret();
  387. }
  388.  
  389. void HexViewer::ScrollVisible(__int64 nVisPos) throw() {
  390.     __int64 nTopLine    = i64TopOffset>>4;
  391.     __int64 nCaretLine    = i64Position>>4;
  392.  
  393.     if (nCaretLine < nTopLine)
  394.         ScrollTopTo((long)nCaretLine);
  395.     else if (nCaretLine >= nTopLine + nCurrentWholeLines)
  396.         ScrollTopTo((long)(nCaretLine - nCurrentWholeLines + 1));
  397. }
  398.  
  399. void HexViewer::MoveCaret() throw() {
  400.     __int64 nTopLine    = i64TopOffset>>4;
  401.     __int64 nCaretLine    = i64Position>>4;
  402.  
  403.     if (nCaretLine < nTopLine || nCaretLine >= nTopLine + nCurrentVisLines) {
  404.         Hide();
  405.         return;
  406.     }
  407.  
  408.     int nLine, nByteOffset, x, y;
  409.  
  410.     nLine            = (int)(nCaretLine - nTopLine);
  411.     nByteOffset        = (int)i64Position & 15;
  412.  
  413.     y = nLine * nLineHeight;
  414.  
  415.     if (bCharMode) {
  416.         x = 14 + 3*16 + 1 + nByteOffset;
  417.     } else {
  418.         x = 14 + 3*nByteOffset;
  419.  
  420.         if (bOddHex)
  421.             ++x;
  422.     }
  423.  
  424.     SetCaretPos(x*nCharWidth, y);
  425.     Show();
  426. }
  427.  
  428. void HexViewer::MoveCaretToByte(__int64 pos) throw() {
  429.     if (pos < 0) {
  430.         bOddHex = false;
  431.         pos = 0;
  432.     } else if (pos >= i64FileSize) {
  433.         pos = i64FileSize - 1;
  434.         bOddHex = !bCharMode;
  435.     }
  436.     i64Position = pos;
  437.     ScrollVisible(pos);
  438.     MoveCaret();
  439. }
  440.  
  441. LRESULT HexViewer::Handle_WM_COMMAND(WPARAM wParam, LPARAM lParam) throw() {
  442.     switch(LOWORD(wParam)) {
  443.     case ID_FILE_EXIT:
  444.         CloseWindow(hwnd);
  445.         break;
  446.     case ID_FILE_CLOSE:
  447.         Close();
  448.         break;
  449.     case ID_FILE_OPEN:
  450.         Open();
  451.         break;
  452.     case ID_FILE_SAVE:
  453.         Commit();
  454.         break;
  455.     case ID_FILE_REVERT:
  456.         if (IDOK==MessageBox(hwnd, "Discard all changes?", g_szHexWarning, MB_OKCANCEL)) {
  457.             HVModifiedLine *pLine;
  458.  
  459.             while(pLine = listMods.RemoveHead())
  460.                 delete pLine;
  461.          
  462.             InvalidateRect(hwnd, NULL, TRUE);
  463.         }
  464.         break;
  465.     case ID_EDIT_JUMP:
  466.         {
  467.             __int64 v1 = i64Position, v2;
  468.  
  469.             if (AskForValues("Jump to address", "Address:", NULL, v1, v2, JumpVerifier))
  470.                 ScrollTopTo((long)(v1>>4));
  471.         }
  472.         break;
  473.     case ID_EDIT_TRUNCATE:
  474.         {
  475.             __int64 v1 = i64Position, v2;
  476.  
  477.             if (AskForValues("Truncate file", "Address:", NULL, v1, v2, TruncateVerifier)) {
  478.                 if ((0xFFFFFFFF==SetFilePointer(hFile, (LONG)v1, (LONG *)&v1 + 1, FILE_BEGIN) && GetLastError()!=NO_ERROR)
  479.                         || !SetEndOfFile(hFile))
  480.                     MyWin32Error("Cannot truncate file: %%s", GetLastError()).post(hwnd, "Error");
  481.  
  482.                 i64FileSize = v1;
  483.  
  484.                 ScrollTopTo((long)(i64TopOffset>>4));
  485.             }
  486.         }
  487.         break;
  488.     case ID_EDIT_EXTRACT:
  489.         Extract();
  490.         break;
  491.  
  492.     case ID_EDIT_FINDNEXT:
  493.         if (hwndFind) {
  494.             SendMessage(hwndFind, WM_COMMAND, IDC_FIND, 0);
  495.             break;
  496.         } else if (pszFindString) {
  497.             Find();
  498.             break;
  499.         }
  500.     case ID_EDIT_FIND:
  501.         if (hwndFind)
  502.             SetForegroundWindow(hwndFind);
  503.         else
  504.             CreateDialogParam(g_hInst, MAKEINTRESOURCE(IDD_HEXVIEWER_FIND), hwnd, FindDlgProc, (LPARAM)this);
  505.         break;
  506.  
  507.     case ID_EDIT_RIFFTREE:
  508.         if (hwndTree)
  509.             DestroyWindow(hwndTree);
  510.         else
  511.             CreateDialogParam(g_hInst, MAKEINTRESOURCE(IDD_HEXVIEWER_RIFFLIST), hwnd, TreeDlgProc, (LPARAM)this);
  512.         break;
  513.  
  514.     case ID_HELP_WHY:
  515.         MessageBox(hwnd,
  516.             "I need a quick way for people to send me parts of files that don't load properly "
  517.             "in VirtualDub, and this is a handy way to do it. Well, that, and it's annoying to "
  518.             "check 3Gb AVI files if your hex editor tries to load the file into memory.",
  519.             "Why is there a hex editor in VirtualDub?",
  520.             MB_OK);
  521.         break;
  522.  
  523.     case ID_HELP_KEYS:
  524.         MessageBox(hwnd,
  525.             "arrow keys/PgUp/PgDn: navigation\n"
  526.             "TAB: switch between ASCII/Hex\n"
  527.             "Backspace: undo",
  528.             "Keyboard commands",
  529.             MB_OK);
  530.         break;
  531.  
  532.     }
  533.  
  534.     return 0;
  535. }
  536.  
  537. static const char hexdig[]="0123456789ABCDEF";
  538.  
  539. LRESULT HexViewer::Handle_WM_PAINT(WPARAM wParam, LPARAM lParam) throw() {
  540.     HDC hdc;
  541.     PAINTSTRUCT ps;
  542.     char buf[128];
  543.     __int64 i64Offset;
  544.     int y;
  545.     RECT r;
  546.     int i;
  547.  
  548.     hdc = BeginPaint(hwnd, &ps);
  549.  
  550.     GetClientRect(hwnd, &r);
  551.     r.left = nCharWidth*79;
  552.     FillRect(hdc, &r, (HBRUSH)(COLOR_WINDOW+1));
  553.  
  554.     i = GetClipBox(hdc, &r);
  555.  
  556.     if (i != ERROR && i != NULLREGION) {
  557.         y = r.top - r.top % nLineHeight;
  558.  
  559.         if (hFile != INVALID_HANDLE_VALUE) {
  560.             HGDIOBJ hfOld;
  561.  
  562.             i64Offset = i64TopOffset + (y/nLineHeight)*16;
  563.  
  564.             hfOld = SelectObject(hdc, hfont ? hfont : GetStockObject(ANSI_FIXED_FONT));
  565.  
  566.             SetTextAlign(hdc, TA_TOP | TA_LEFT);
  567.             SetBkMode(hdc, OPAQUE);
  568.  
  569.             while(y < r.bottom+nLineHeight-1 && i64Offset < i64FileSize) {
  570.                 HVModifiedLine *pModLine = FindModLine(i64Offset);
  571.                 const char *data;
  572.                 char *s;
  573.                 int i, len;
  574.  
  575.                 if (pModLine)
  576.                     data = pModLine->data;
  577.                 else {
  578.                     data = FillRowCache(i64Offset);
  579.                 }
  580.  
  581.                 len = 16;
  582.  
  583.                 if ((i64Offset & -16i64) == (i64FileSize & -16i64))
  584.                     len = (int)i64FileSize & 15;
  585.  
  586.                 s = buf + sprintf(buf, "%12I64X: ", i64Offset);
  587.  
  588.                 for(i=0; i<len; i++) {
  589.                     *s++ = hexdig[(unsigned char)data[i] >> 4];
  590.                     *s++ = hexdig[data[i] & 15];
  591.                     *s++ = ' ';
  592.                 }
  593.  
  594.                 while(i<16) {
  595.                     *s++ = ' ';
  596.                     *s++ = ' ';
  597.                     *s++ = ' ';
  598.                     ++i;
  599.                 }
  600.  
  601.                 s[-8*3-1] = '-';
  602.                 *s++ = ' ';
  603.  
  604.                 for(i=0; i<len; i++) {
  605.                     if (data[i]>=0x20 && data[i]<0x7f)
  606.                         *s++ = data[i];
  607.                     else
  608.                         *s++ = '.';
  609.                 }
  610.  
  611.                 TextOut(hdc, 0, y, buf, s - buf);
  612.  
  613.                 // Draw modified constants in blue.
  614.  
  615.                 if (pModLine) {
  616.                     COLORREF crOldTextColor = SetTextColor(hdc, 0xFF0000);
  617.  
  618.                     for(i=0; i<16; i++)
  619.                         if (!(pModLine->mod_flags & (1<<i))) {
  620.                             buf[14 + i*3] = ' ';
  621.                             buf[15 + i*3] = ' ';
  622.                             buf[63+i] =  ' ';
  623.                         }
  624.  
  625.                     buf[37] = ' ';
  626.  
  627.                     SetBkMode(hdc, TRANSPARENT);
  628.  
  629.                     TextOut(hdc, nCharWidth*14, y, buf+14, 16*3-1);
  630.                     TextOut(hdc, nCharWidth*63, y, buf+63, 16);
  631.  
  632.                     SetBkMode(hdc, OPAQUE);
  633.  
  634.                     SetTextColor(hdc, crOldTextColor);
  635.                 }
  636.  
  637.                 y += nLineHeight;
  638.                 i64Offset += 16;
  639.             }
  640.  
  641.             SelectObject(hdc, hfOld);
  642.         }
  643.  
  644.         if (y < r.bottom+nLineHeight-1) {
  645.             GetClientRect(hwnd, &r);
  646.             r.top = y;
  647.             FillRect(hdc, &r, (HBRUSH)(COLOR_WINDOW+1));
  648.         }
  649.     }
  650.     EndPaint(hwnd, &ps);
  651.  
  652.     return 0;
  653. }
  654.  
  655. LRESULT HexViewer::Handle_WM_VSCROLL(WPARAM wParam, LPARAM lParam) throw() {
  656.     SCROLLINFO si;
  657.  
  658.     switch(LOWORD(wParam)) {
  659.     case SB_BOTTOM:        ScrollTopTo(nLineLimit); break;
  660.     case SB_TOP:        ScrollTopTo(0); break;
  661.     case SB_LINEUP:        ScrollTopTo((long)(i64TopOffset>>4) - 1); break;
  662.     case SB_LINEDOWN:    ScrollTopTo((long)(i64TopOffset>>4) + 1); break;
  663.     case SB_PAGEUP:        ScrollTopTo((long)(i64TopOffset>>4) - (nCurrentVisLines - 1)); break;
  664.     case SB_PAGEDOWN:    ScrollTopTo((long)(i64TopOffset>>4) + (nCurrentVisLines - 1)); break;
  665.     case SB_THUMBTRACK:
  666.         si.cbSize = sizeof(SCROLLINFO);
  667.         si.fMask = SIF_TRACKPOS;
  668.         GetScrollInfo(hwnd, SB_VERT, &si);
  669.         ScrollTopTo(si.nTrackPos);
  670.         break;
  671.     }
  672.  
  673.     return 0;
  674. }
  675.  
  676. LRESULT HexViewer::Handle_WM_SIZE(WPARAM wParam, LPARAM lParam) throw() {
  677.     RECT r;
  678.  
  679.     GetClientRect(hwnd, &r);
  680.  
  681.     nCurrentWholeLines    = (r.bottom - r.top) / nLineHeight; 
  682.     nCurrentVisLines    = (r.bottom - r.top + nLineHeight - 1) / nLineHeight;
  683.  
  684.     return 0;
  685. }
  686.  
  687. LRESULT HexViewer::Handle_WM_MOUSEWHEEL(WPARAM wParam, LPARAM lParam) {
  688.     int iNewDelta, nScroll;
  689.     
  690.     iNewDelta = iMouseWheelDelta - (signed short)HIWORD(wParam);
  691.     nScroll = iNewDelta / WHEEL_DELTA;
  692.  
  693.     if (nScroll) {
  694.         ScrollTopTo((long)(i64TopOffset>>4) + nScroll);
  695.         iNewDelta -= WHEEL_DELTA * nScroll;
  696.     }
  697.  
  698.     iMouseWheelDelta = iNewDelta;
  699.  
  700.     return 0;
  701. }
  702.  
  703. LRESULT HexViewer::Handle_WM_KEYDOWN(WPARAM wParam, LPARAM lParam) throw() {
  704.     switch(wParam) {
  705.     case VK_UP:
  706.         MoveCaretToByte(i64Position-16);
  707.         break;
  708.     case VK_DOWN:
  709.         MoveCaretToByte(i64Position+16);
  710.         break;
  711.     case VK_LEFT:
  712.         if (bCharMode || (bOddHex = !bOddHex))
  713.             MoveCaretToByte(i64Position-1);
  714.         else
  715.             MoveCaretToByte(i64Position);
  716.         break;
  717.     case VK_RIGHT:
  718.         if (bCharMode || !(bOddHex = !bOddHex))
  719.             MoveCaretToByte(i64Position+1);
  720.         else
  721.             MoveCaretToByte(i64Position);
  722.         break;
  723.     case VK_PRIOR:
  724.         MoveCaretToByte(i64Position - (nCurrentVisLines-1)*16);
  725.         break;
  726.     case VK_NEXT:
  727.         MoveCaretToByte(i64Position + (nCurrentVisLines-1)*16);
  728.         break;
  729.     case VK_HOME:
  730.         bOddHex = false;
  731.         if ((signed short)GetKeyState(VK_CONTROL)<0)
  732.             MoveCaretToByte(0);
  733.         else
  734.             MoveCaretToByte(i64Position & -16i64);
  735.         break;
  736.     case VK_END:
  737.         bOddHex = true;
  738.         if ((signed short)GetKeyState(VK_CONTROL)<0)
  739.             MoveCaretToByte(i64FileSize - 1);
  740.         else
  741.             MoveCaretToByte(i64Position | 15);
  742.         break;
  743.     case VK_TAB:
  744.         bCharMode = !bCharMode;
  745.         bOddHex = false;
  746.         MoveCaretToByte(i64Position);
  747.         break;
  748.     case VK_F3:
  749.         if (hFile != INVALID_HANDLE_VALUE)
  750.             Handle_WM_COMMAND(ID_EDIT_FINDNEXT, 0);
  751.         break;
  752.     case 'F':
  753.         if (hFile != INVALID_HANDLE_VALUE)
  754.             if (GetKeyState(VK_CONTROL)<0)
  755.                 Handle_WM_COMMAND(ID_EDIT_FIND, 0);
  756.         break;
  757.     case 'R':
  758.         if (hFile != INVALID_HANDLE_VALUE)
  759.             if (GetKeyState(VK_CONTROL)<0)
  760.                 Handle_WM_COMMAND(ID_EDIT_RIFFTREE, 0);
  761.     }
  762.  
  763.     return 0;
  764. }
  765.  
  766. LRESULT HexViewer::Handle_WM_LBUTTONDOWN(WPARAM wParam, LPARAM lParam) throw() {
  767.     int x, y;
  768.  
  769.     x = LOWORD(lParam) / nCharWidth;
  770.     y = HIWORD(lParam) / nLineHeight;
  771.  
  772.    if (x < 14)
  773.       x = 14;
  774.    else if (x >= 63+16)
  775.       x = 63+15;
  776.  
  777.     if (x >= 14 && x < 61) {
  778.         x -= 13;
  779.  
  780.         bCharMode = false;
  781.         bOddHex = false;
  782.  
  783.         if (x%3 == 2)
  784.             bOddHex = true;
  785.  
  786.         x = x/3;
  787.     } else if (x >= 63 && x < 63+16) {
  788.         bCharMode = true;
  789.         bOddHex = false;
  790.  
  791.         x -= 63;
  792.     } else
  793.         return 0;
  794.  
  795.     MoveCaretToByte(i64TopOffset + y*16 + x);
  796.  
  797.     return 0;
  798. }
  799.  
  800. LRESULT HexViewer::Handle_WM_CHAR(WPARAM wParam, LPARAM lParam) throw() {
  801.     int key = wParam;
  802.  
  803.     if (!bEnableWrite)
  804.         return 0;
  805.  
  806.     if (key == '\b') {
  807.         HVModifiedLine *pLine;
  808.         __int64 i64Offset;
  809.  
  810.         if (bCharMode || !bOddHex)
  811.             MoveCaretToByte(i64Position-1);
  812.         else
  813.             MoveCaretToByte(i64Position);
  814.  
  815.         bOddHex = false;
  816.  
  817.         i64Offset = i64Position & -16i64;
  818.  
  819.         if (pLine = FindModLine(i64Offset)) {
  820.             // Revert byte.
  821.  
  822.             int offset = (int)i64Position & 15;
  823.  
  824.             pLine->data[offset] = FillRowCache(i64Offset)[offset];
  825.             pLine->mod_flags &= ~(1<<offset);
  826.  
  827.             if (!pLine->mod_flags) {
  828.                 pLine->Remove();
  829.                 delete pLine;
  830.             }
  831.  
  832.             InvalidateLine(i64Offset);
  833.         }
  834.  
  835.         return 0;
  836.     }
  837.  
  838.     if (!isprint(key) || (!bCharMode && !isxdigit(key)))
  839.         return 0;
  840.  
  841.     // Fetch the mod line.
  842.  
  843.     __int64 i64Offset = i64Position & -16i64;
  844.     HVModifiedLine *pLine = FindModLine(i64Offset);
  845.  
  846.     if (!pLine) {
  847.         // Line not resident -- fetch from disk and create.
  848.  
  849.         DWORD dwActual;
  850.  
  851.         pLine = new HVModifiedLine(i64Offset);
  852.  
  853.         SetFilePointer(hFile, (LONG)i64Offset, (LONG *)&i64Offset + 1, FILE_BEGIN);
  854.  
  855.         ReadFile(hFile, pLine->data, 16, &dwActual, NULL);
  856.  
  857.         listMods.AddTail(pLine);
  858.     }
  859.  
  860.     // Modify the appropriate byte and redraw the line.
  861.  
  862.     int offset = (int)i64Position & 15;
  863.  
  864.     if (bCharMode)
  865.         pLine->data[offset] = key;
  866.     else {
  867.         int v = toupper(key) - '0';
  868.         if (v > 9)
  869.             v -= 7;
  870.  
  871.         if (bOddHex)
  872.             pLine->data[offset] = (pLine->data[offset]&0xf0) + v;
  873.         else
  874.             pLine->data[offset] = (pLine->data[offset]&0x0f) + (v<<4);
  875.     }
  876.     pLine->mod_flags |= 1<<offset;
  877.  
  878.     // invalidate row cache
  879.  
  880.     i64RowCacheAddr = -1;
  881.  
  882.     InvalidateLine(i64Position);
  883.  
  884.     // Send a RIGHT keypress to advance.
  885.  
  886.     SendMessage(hwnd, WM_KEYDOWN, VK_RIGHT, 0);
  887.  
  888.     return 0;
  889. }
  890.  
  891. ////////////////////////////
  892.  
  893. struct HexViewerAskData {
  894.     HexViewer *thisPtr;
  895.     const char *title, *name1, *name2;
  896.     __int64 v1, v2;
  897.     int (HexViewer::*verifier)(HWND, __int64 v1, __int64 v2);
  898. };
  899.  
  900. BOOL CALLBACK HexViewer::AskForValuesDlgProc(HWND hdlg, UINT msg, WPARAM wParam, LPARAM lParam) throw() {
  901.     HexViewerAskData *pData = (HexViewerAskData *)GetWindowLong(hdlg, DWL_USER);
  902.     char buf[32];
  903.  
  904.     switch(msg) {
  905.     case WM_INITDIALOG:
  906.         pData = (HexViewerAskData *)lParam;
  907.         SetWindowLong(hdlg, DWL_USER, lParam);
  908.  
  909.         SetWindowText(hdlg, pData->title);
  910.         sprintf(buf, "%I64X", pData->v1);
  911.         SetDlgItemText(hdlg, IDC_EDIT_ADDRESS1, buf);
  912.         SendDlgItemMessage(hdlg, IDC_EDIT_ADDRESS1, EM_LIMITTEXT, 16, 0);
  913.         SetDlgItemText(hdlg, IDC_STATIC_ADDRESS1, pData->name1);
  914.         if (pData->name2) {
  915.             sprintf(buf, "%I64X", pData->v2);
  916.             SetDlgItemText(hdlg, IDC_EDIT_ADDRESS2, buf);
  917.             SendDlgItemMessage(hdlg, IDC_EDIT_ADDRESS1, EM_LIMITTEXT, 16, 0);
  918.             SetDlgItemText(hdlg, IDC_STATIC_ADDRESS2, pData->name2);
  919.         } else {
  920.             ShowWindow(GetDlgItem(hdlg, IDC_EDIT_ADDRESS2), SW_HIDE);
  921.             ShowWindow(GetDlgItem(hdlg, IDC_STATIC_ADDRESS2), SW_HIDE);
  922.         }
  923.         return TRUE;
  924.  
  925.     case WM_COMMAND:
  926.         switch(LOWORD(wParam)) {
  927.         case IDCANCEL:
  928.             EndDialog(hdlg, 0);
  929.             break;
  930.         case IDOK:
  931.             {
  932.                 __int64 v1=0, v2=0;
  933.                 const char *s, *t;
  934.                 char c;
  935.                 int i;
  936.  
  937.                 GetDlgItemText(hdlg, IDC_EDIT_ADDRESS1, buf, sizeof buf);
  938.  
  939.                 s = buf;
  940.                 while(c=*s++) {
  941.                     if (!(t=strchr(hexdig, toupper(c)))) {
  942.                         SetFocus(GetDlgItem(hdlg, IDC_EDIT_ADDRESS1));
  943.                         MessageBeep(MB_ICONEXCLAMATION);
  944.                         return TRUE;
  945.                     }
  946.  
  947.                     v1 = (v1<<4) | (t-hexdig);
  948.                 }
  949.  
  950.                 if (pData->name2) {
  951.                     GetDlgItemText(hdlg, IDC_EDIT_ADDRESS2, buf, sizeof buf);
  952.  
  953.                     s = buf;
  954.                     while(c=*s++) {
  955.                         if (!(t=strchr(hexdig, toupper(c)))) {
  956.                             SetFocus(GetDlgItem(hdlg, IDC_EDIT_ADDRESS2));
  957.                             MessageBeep(MB_ICONEXCLAMATION);
  958.                             return TRUE;
  959.                         }
  960.  
  961.                         v2 = (v2<<4) | (t-hexdig);
  962.                     }
  963.                 }
  964.  
  965.                 if (i = (pData->thisPtr->*(pData->verifier))(hdlg, v1, v2)) {
  966.                     if (i>=0) {
  967.                         SetFocus(GetDlgItem(hdlg, i==1?IDC_EDIT_ADDRESS1:IDC_EDIT_ADDRESS2));
  968.                         MessageBeep(MB_ICONEXCLAMATION);
  969.                     }
  970.                     return TRUE;
  971.                 }
  972.  
  973.                 pData->v1 = v1;
  974.                 pData->v2 = v2;
  975.             }
  976.             EndDialog(hdlg, 1);
  977.             break;
  978.         }
  979.         return TRUE;
  980.     }
  981.     return FALSE;
  982. }
  983.  
  984. bool HexViewer::AskForValues(const char *title, const char *name1, const char *name2, __int64& v1, __int64& v2, int (HexViewer::*verifier)(HWND hdlg, __int64 v1, __int64 v2)) throw() {
  985.     HexViewerAskData hvad;
  986.  
  987.     hvad.thisPtr = this;
  988.     hvad.title = title;
  989.     hvad.name1 = name1;
  990.     hvad.name2 = name2;
  991.     hvad.v1 = v1;
  992.     hvad.v2 = v2;
  993.     hvad.verifier = verifier;
  994.  
  995.     if (DialogBoxParam(g_hInst, MAKEINTRESOURCE(IDD_HEXVIEWER), hwnd, AskForValuesDlgProc, (LPARAM)&hvad)) {
  996.         v1 = hvad.v1;
  997.         v2 = hvad.v2;
  998.         return true;
  999.     }
  1000.     return false;
  1001. }
  1002.  
  1003. int HexViewer::JumpVerifier(HWND hdlg, __int64 v1, __int64 v2) throw() {
  1004.     if (v1>i64FileSize)
  1005.         return 1;
  1006.  
  1007.     return 0;
  1008. }
  1009.  
  1010. int HexViewer::ExtractVerifier(HWND hdlg, __int64 v1, __int64 v2) throw() {
  1011.     if (v1 > i64FileSize)
  1012.         return 1;
  1013.  
  1014.     if (v1+v2 > i64FileSize)
  1015.         return 2;
  1016.  
  1017.     return 0;
  1018. }
  1019.  
  1020. int HexViewer::TruncateVerifier(HWND hdlg, __int64 v1, __int64 v2) throw() {
  1021.     int r;
  1022.  
  1023.     if (v1 < i64FileSize)
  1024.         r = MessageBox(hdlg, "You will lose all data past the specified address. Are you sure you want to truncate the file?", "Warning", MB_ICONEXCLAMATION|MB_YESNO);
  1025.     else if (v1 > i64FileSize)
  1026.         r = MessageBox(hdlg, "You have specified an address past the end of the file. Extend file to specified address?", "Warning", MB_ICONEXCLAMATION|MB_YESNO);
  1027.  
  1028.     return r==IDYES ? 0 : -1;
  1029. }
  1030.  
  1031. void HexViewer::Extract() throw() {
  1032.     __int64 v1 = i64TopOffset, v2=0x1000;
  1033.  
  1034.     if (AskForValues("Extract file segment", "Address:", "Length:", v1, v2, ExtractVerifier)) {
  1035.         char szName[MAX_PATH];
  1036.         OPENFILENAME ofn;
  1037.  
  1038.         szName[0] = 0;
  1039.  
  1040.         ofn.lStructSize            = sizeof(OPENFILENAME);
  1041.         ofn.hwndOwner            = hwnd;
  1042.         ofn.lpstrFilter            = "All files (*.*)\0*.*\0";
  1043.         ofn.lpstrCustomFilter    = NULL;
  1044.         ofn.nFilterIndex        = 1;
  1045.         ofn.lpstrFile            = szName;
  1046.         ofn.nMaxFile            = sizeof szName;
  1047.         ofn.lpstrFileTitle        = NULL;
  1048.         ofn.lpstrInitialDir        = NULL;
  1049.         ofn.lpstrTitle            = NULL;
  1050.         ofn.Flags                = OFN_EXPLORER | OFN_ENABLESIZING | OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT;
  1051.         ofn.lpstrDefExt            = NULL;
  1052.  
  1053.         if (GetSaveFileName(&ofn)) {
  1054.             HANDLE hFile2 = INVALID_HANDLE_VALUE;
  1055.             char *pBuf = NULL;
  1056.  
  1057.             try {
  1058.                 __int64 fpos = 0;
  1059.  
  1060.                 if (0xFFFFFFFF==SetFilePointer(hFile, (LONG)v1, (LONG *)&v1 + 1, FILE_BEGIN) && GetLastError()!=NO_ERROR)
  1061.                     throw GetLastError();
  1062.  
  1063.                 pBuf = new char[65536];
  1064.  
  1065.                 if (!pBuf)
  1066.                     throw MyMemoryError();
  1067.  
  1068.                 hFile2 = CreateFile(szName, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL|FILE_FLAG_SEQUENTIAL_SCAN, NULL);
  1069.  
  1070.                 if (hFile2 == INVALID_HANDLE_VALUE)
  1071.                     throw GetLastError();
  1072.  
  1073.                 if ((0xFFFFFFFF==SetFilePointer(hFile2, (LONG)v2, (LONG *)&v2 + 1, FILE_BEGIN) && GetLastError()!=NO_ERROR)
  1074.                     || !SetEndOfFile(hFile2))
  1075.                     throw GetLastError();
  1076.  
  1077.                 if (0xFFFFFFFF==SetFilePointer(hFile2, 0, NULL, FILE_BEGIN) && GetLastError()!=NO_ERROR)
  1078.                     throw GetLastError();
  1079.  
  1080.                 ProgressDialog pd(hwnd, "Extract segment", "Copying data range", (long)(v2>>10), TRUE);
  1081.                 pd.setValueFormat("%dK of %dK");
  1082.  
  1083.                 while(v2 > 0) {
  1084.                     DWORD dwToCopy = (DWORD)v2, dwActual;
  1085.  
  1086.                     if (dwToCopy > 65536)
  1087.                         dwToCopy = 65536;
  1088.  
  1089.                     pd.check();
  1090.  
  1091.                     if (!ReadFile(hFile, pBuf, dwToCopy, &dwActual, NULL) || dwActual < dwToCopy)
  1092.                         throw GetLastError();
  1093.  
  1094.                     if (!WriteFile(hFile2, pBuf, dwToCopy, &dwActual, NULL) || dwActual < dwToCopy)
  1095.                         throw GetLastError();
  1096.  
  1097.                     v2 -= dwActual;
  1098.  
  1099.                     pd.advance((long)((fpos += dwActual)>>10));
  1100.                 }
  1101.  
  1102.                 if (!CloseHandle(hFile2))
  1103.                     throw GetLastError();
  1104.  
  1105.                 hFile2 = INVALID_HANDLE_VALUE;
  1106.             } catch(DWORD dw) {
  1107.                 MyWin32Error("Cannot create extract file: %%s", dw).post(hwnd, "Error");
  1108.             } catch(MyUserAbortError e) {
  1109.                 SetEndOfFile(hFile2);
  1110.             }
  1111.  
  1112.             if (hFile2 != INVALID_HANDLE_VALUE)
  1113.                 CloseHandle(hFile2);
  1114.  
  1115.             delete[] pBuf;
  1116.         }
  1117.     }
  1118. }
  1119.  
  1120. void HexViewer::Find() throw() {
  1121.     if (!nFindLength || !pszFindString) {
  1122.         SendMessage(hwnd, WM_COMMAND, ID_EDIT_FIND, 0);
  1123.         return;
  1124.     }
  1125.  
  1126.     int *next = new int[nFindLength+1];
  1127.     char *searchbuffer = new char[65536];
  1128.     char *revstring = new char[nFindLength];
  1129.     char *findstring = pszFindString;
  1130.     int i,j;
  1131.  
  1132.     if (!next || !searchbuffer) {
  1133.         delete[] next;
  1134.         delete[] searchbuffer;
  1135.         delete[] revstring;
  1136.         return;
  1137.     }
  1138.  
  1139.     if (bFindReverse) {
  1140.         for(i=0; i<nFindLength; ++i)
  1141.             revstring[i] = pszFindString[nFindLength-i-1];
  1142.  
  1143.         findstring = revstring;
  1144.     }
  1145.  
  1146.     // Initialize next list (Knuth-Morris-Pratt algorithm):
  1147.  
  1148.     next[0] = -1;
  1149.     i = 0;
  1150.     j = -1;
  1151.  
  1152.     do {
  1153.         if (j==-1 || findstring[i] == findstring[j]) {
  1154.             ++i;
  1155.             ++j;
  1156.             next[i] = (findstring[i] == findstring[j]) ? next[j] : j;
  1157.         } else
  1158.             j = next[j];
  1159.     } while(i < nFindLength);
  1160.  
  1161.     // Begin paging in sectors from disk.
  1162.  
  1163.     int limit=0;
  1164.     int size = 512;
  1165.     __int64 pos = i64Position;
  1166.     __int64 posbase;
  1167.     bool bLastPartial = false;
  1168.  
  1169.     ProgressDialog pd(GetForegroundWindow(), "Find",
  1170.         bFindReverse?"Reverse searching for string":"Forward searching for string", (long)((i64Position+1048575)>>10), TRUE);
  1171.     pd.setValueFormat("%dK of %dK");
  1172.  
  1173.     i = 0;
  1174.     j = -1;    // this causes the first char to be skipped
  1175.  
  1176.     try {
  1177.         if (bFindReverse) {
  1178.             List2<HVModifiedLine>::rvit itML = listMods.end();
  1179.  
  1180.             while(pos >= 0) {
  1181.                 {
  1182.                     DWORD dwActual;
  1183.  
  1184.                     i = pos & 511;
  1185.  
  1186.                     pos &= ~511i64;
  1187.  
  1188.                     SetFilePointer(hFile, (LONG)pos, (LONG *)&pos + 1, FILE_BEGIN);
  1189.                     if (!ReadFile(hFile, searchbuffer, size, &dwActual, NULL))
  1190.                         break;
  1191.  
  1192.                     // we're overloading the bLastPartial variable as a 'first' flag....
  1193.  
  1194.                     if (!bLastPartial && !dwActual)
  1195.                         goto xit;
  1196.  
  1197.                     bLastPartial = true;
  1198.  
  1199.                     limit = (int)dwActual;
  1200.  
  1201.                     if (pos + limit > i64Position)
  1202.                         limit = i64Position - pos;
  1203.  
  1204.                     if (!i) 
  1205.                         i = limit;
  1206.  
  1207.                     while(itML && itML->address >= pos+limit)
  1208.                         --itML;
  1209.  
  1210.                     while(itML && itML->address >= pos) {
  1211.                         memcpy(searchbuffer + (long)(itML->address - pos), itML->data, 16);
  1212.                         --itML;
  1213.                     }
  1214.  
  1215.                     posbase = pos;
  1216.                     pos -= size;
  1217.  
  1218.                     if (size < 65536 && !((long)pos & (size*2-1)))
  1219.                         size += size;
  1220.                 }
  1221.  
  1222.                 if (bFindCaseInsensitive)
  1223.                     while(i >= 0) {
  1224.                         if (j == -1 || toupper((unsigned char)searchbuffer[i]) == toupper((unsigned char)findstring[j])) {
  1225.                             --i;
  1226.                             ++j;
  1227.  
  1228.                             if (j >= nFindLength) {
  1229.                                 MoveCaretToByte(posbase+i+1);
  1230.                                 goto xit;
  1231.                             }
  1232.                         } else
  1233.                             j = next[j];
  1234.                     }
  1235.                 else
  1236.                     while(i >= 0) {
  1237.                         if (j == -1 || searchbuffer[i] == findstring[j]) {
  1238.                             --i;
  1239.                             ++j;
  1240.  
  1241.                             if (j >= nFindLength) {
  1242.                                 MoveCaretToByte(posbase+i+1);
  1243.                                 goto xit;
  1244.                             }
  1245.                         } else
  1246.                             j = next[j];
  1247.                     }
  1248.  
  1249.                 pd.advance((long)((i64Position - pos)>>10));
  1250.                 pd.check();
  1251.             }
  1252.         } else {
  1253.             List2<HVModifiedLine>::fwit itML = listMods.begin();
  1254.  
  1255.             for(;;) {
  1256.                 {
  1257.                     DWORD dwActual;
  1258.  
  1259.                     if (bLastPartial)
  1260.                         break;
  1261.  
  1262.                     i = pos & 511;
  1263.  
  1264.                     pos &= ~511i64;
  1265.  
  1266.                     SetFilePointer(hFile, (LONG)pos, (LONG *)&pos + 1, FILE_BEGIN);
  1267.                     if (!ReadFile(hFile, searchbuffer, size, &dwActual, NULL))
  1268.                         break;
  1269.  
  1270.                     limit = (int)dwActual;
  1271.  
  1272.                     while(itML && itML->address < pos)
  1273.                         ++itML;
  1274.  
  1275.                     while(itML && itML->address < pos+limit) {
  1276.                         memcpy(searchbuffer + (long)(itML->address - pos), itML->data, 16);
  1277.                         ++itML;
  1278.                     }
  1279.  
  1280.                     if (dwActual < size)
  1281.                         bLastPartial = true;
  1282.  
  1283.                     posbase = pos;
  1284.                     pos += limit;
  1285.  
  1286.                     if (size < 65536 && !((long)pos & (size*2-1)))
  1287.                         size += size;
  1288.                 }
  1289.  
  1290.                 if (bFindCaseInsensitive)
  1291.                     while(i < limit) {
  1292.                         if (j == -1 || toupper((unsigned char)searchbuffer[i]) == toupper((unsigned char)findstring[j])) {
  1293.                             ++i;
  1294.                             ++j;
  1295.  
  1296.                             if (j >= nFindLength) {
  1297.                                 MoveCaretToByte(pos-limit+i-nFindLength);
  1298.                                 goto xit;
  1299.                             }
  1300.                         } else
  1301.                             j = next[j];
  1302.                     }
  1303.                 else
  1304.                     while(i < limit) {
  1305.                         if (j == -1 || searchbuffer[i] == findstring[j]) {
  1306.                             ++i;
  1307.                             ++j;
  1308.  
  1309.                             if (j >= nFindLength) {
  1310.                                 MoveCaretToByte(posbase+i-nFindLength);
  1311.                                 goto xit;
  1312.                             }
  1313.                         } else
  1314.                             j = next[j];
  1315.                     }
  1316.  
  1317.                 pd.advance((long)((pos - i64Position)>>10));
  1318.                 pd.check();
  1319.             }
  1320.         }
  1321.  
  1322.         pd.close();
  1323.  
  1324.         MessageBox(GetForegroundWindow(), "Search string not found", "Find", MB_OK);
  1325. xit:
  1326.         ;
  1327.     } catch(MyUserAbortError) {
  1328.     }
  1329.  
  1330.     delete[] next;
  1331.     delete[] searchbuffer;
  1332.     delete[] revstring;
  1333. }
  1334.  
  1335. ////////////////////////////
  1336.  
  1337. LRESULT APIENTRY HexViewer::HexViewerWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) throw() {
  1338.     HexViewer *pcd = (HexViewer *)GetWindowLong(hwnd, 0);
  1339.  
  1340.     switch(msg) {
  1341.  
  1342.     case WM_NCCREATE:
  1343.         if (!(pcd = new HexViewer(hwnd)))
  1344.             return FALSE;
  1345.  
  1346.         SetWindowLong(hwnd, 0, (LONG)pcd);
  1347.         return DefWindowProc(hwnd, msg, wParam, lParam);
  1348.  
  1349.     case WM_CREATE:
  1350.         pcd->Init();
  1351.         return 0;
  1352.  
  1353.     case WM_SIZE:
  1354.         return pcd->Handle_WM_SIZE(wParam, lParam);
  1355.  
  1356.     case WM_DESTROY:
  1357.         delete pcd;
  1358.         SetWindowLong(hwnd, 0, 0);
  1359.         break;
  1360.  
  1361.     case WM_COMMAND:
  1362.         return pcd->Handle_WM_COMMAND(wParam, lParam);
  1363.  
  1364.     case WM_PAINT:
  1365.         return pcd->Handle_WM_PAINT(wParam, lParam);
  1366.  
  1367.     case WM_MOUSEWHEEL:
  1368.         return pcd->Handle_WM_MOUSEWHEEL(wParam, lParam);
  1369.  
  1370.     case WM_KEYDOWN:
  1371.         return pcd->Handle_WM_KEYDOWN(wParam, lParam);
  1372.  
  1373.     case WM_CLOSE:
  1374.         DestroyWindow(hwnd);
  1375.         return 0;
  1376.  
  1377.     case WM_VSCROLL:
  1378.         return pcd->Handle_WM_VSCROLL(wParam, lParam);
  1379.  
  1380.     case WM_SETFOCUS:
  1381.         CreateCaret(hwnd, NULL, pcd->nCharWidth, pcd->nLineHeight);
  1382.         pcd->bCaretHidden = true;
  1383.         pcd->MoveCaret();
  1384.         return 0;
  1385.  
  1386.     case WM_KILLFOCUS:
  1387.         DestroyCaret();
  1388.         return 0;
  1389.  
  1390.     case WM_LBUTTONDOWN:
  1391.         return pcd->Handle_WM_LBUTTONDOWN(wParam, lParam);
  1392.  
  1393.     case WM_CHAR:
  1394.         return pcd->Handle_WM_CHAR(wParam, lParam);
  1395.  
  1396.     case WM_INITMENU:
  1397.         {
  1398.             DWORD dwEnableFlags = (pcd->hFile != INVALID_HANDLE_VALUE && pcd->bEnableWrite ? (MF_BYCOMMAND|MF_ENABLED) : (MF_BYCOMMAND|MF_GRAYED));
  1399.             HMENU hMenu = (HMENU)wParam;
  1400.  
  1401.             EnableMenuItem(hMenu,ID_FILE_SAVE, dwEnableFlags);
  1402.             EnableMenuItem(hMenu,ID_FILE_REVERT, dwEnableFlags);
  1403.             EnableMenuItem(hMenu,ID_EDIT_JUMP, dwEnableFlags);
  1404.             EnableMenuItem(hMenu,ID_EDIT_TRUNCATE, dwEnableFlags);
  1405.             EnableMenuItem(hMenu,ID_EDIT_EXTRACT, dwEnableFlags);
  1406.  
  1407.             dwEnableFlags = (pcd->hFile != INVALID_HANDLE_VALUE ? (MF_BYCOMMAND|MF_ENABLED) : (MF_BYCOMMAND|MF_GRAYED));
  1408.  
  1409.             EnableMenuItem(hMenu,ID_EDIT_RIFFTREE, dwEnableFlags);
  1410.             EnableMenuItem(hMenu,ID_EDIT_FIND, dwEnableFlags);
  1411.             EnableMenuItem(hMenu,ID_EDIT_FINDNEXT, dwEnableFlags);
  1412.             EnableMenuItem(hMenu,ID_FILE_CLOSE, dwEnableFlags);
  1413.  
  1414.             CheckMenuItem(hMenu, ID_EDIT_RIFFTREE, pcd->hwndTree ? MF_BYCOMMAND|MF_CHECKED : MF_BYCOMMAND|MF_UNCHECKED);
  1415.         }
  1416.         return 0;
  1417.  
  1418.     default:
  1419.         return DefWindowProc(hwnd, msg, wParam, lParam);
  1420.     }
  1421.     return 0;
  1422. }
  1423.  
  1424. BOOL APIENTRY HexViewer::FindDlgProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) throw() {
  1425.     HexViewer *pcd = (HexViewer *)GetWindowLong(hwnd, DWL_USER);
  1426.  
  1427.     switch(msg) {
  1428.     case WM_INITDIALOG:
  1429.         SetWindowLong(hwnd, DWL_USER, lParam);
  1430.         pcd = (HexViewer *)lParam;
  1431.         pcd->hwndFind = hwnd;
  1432.         pcd->mdnFind.hdlg = hwnd;
  1433.         guiAddModelessDialog(&pcd->mdnFind);
  1434.  
  1435.         if (pcd->pszFindString) {
  1436.             if (pcd->bFindHex) {
  1437.                 char *text = new char[pcd->nFindLength*3];
  1438.  
  1439.                 if (text) {
  1440.                     for(int i=0; i<pcd->nFindLength; ++i) {
  1441.                         int c = (unsigned char)pcd->pszFindString[i];
  1442.  
  1443.                         text[i*3+0] = hexdig[c>>4];
  1444.                         text[i*3+1] = hexdig[c&15];
  1445.                         text[i*3+2] = ' ';
  1446.                     }
  1447.                     text[i*3-1] = 0;
  1448.  
  1449.                     SetDlgItemText(hwnd, IDC_STRING, text);
  1450.                 }
  1451.                 CheckDlgButton(hwnd, IDC_HEX, BST_CHECKED);
  1452.             } else {
  1453.                 SetDlgItemText(hwnd, IDC_STRING, pcd->pszFindString);
  1454.             }
  1455.  
  1456.             if (pcd->bFindCaseInsensitive)
  1457.                 CheckDlgButton(hwnd, IDC_CASELESS, BST_CHECKED);
  1458.  
  1459.         }
  1460.  
  1461.         if (pcd->bFindReverse)
  1462.             CheckDlgButton(hwnd, IDC_UP, BST_CHECKED);
  1463.         else
  1464.             CheckDlgButton(hwnd, IDC_DOWN, BST_CHECKED);
  1465.  
  1466.         SetFocus(GetDlgItem(hwnd, IDC_STRING));
  1467.         return FALSE;
  1468.  
  1469.     case WM_COMMAND:
  1470.         switch(LOWORD(wParam)) {
  1471.         case IDCANCEL:
  1472.             DestroyWindow(hwnd);
  1473.             break;
  1474.  
  1475.         case IDC_FIND:
  1476.             {
  1477.                 HWND hwndEdit = GetDlgItem(hwnd, IDC_STRING);
  1478.                 int l = GetWindowTextLength(hwndEdit);
  1479.  
  1480.                 pcd->bFindHex = !!IsDlgButtonChecked(hwnd, IDC_HEX);
  1481.                 pcd->bFindCaseInsensitive = !!IsDlgButtonChecked(hwnd, IDC_CASELESS);
  1482.                 pcd->bFindReverse = !!IsDlgButtonChecked(hwnd, IDC_UP);
  1483.  
  1484.                 if (l) {
  1485.                     char *text = new char[l+1];
  1486.  
  1487.                     if (GetWindowText(hwndEdit, text, l+1)) {
  1488.                         if (IsDlgButtonChecked(hwnd, IDC_HEX)) {
  1489.                             char *s = text, *s2;
  1490.                             char *t = text;
  1491.                             int c;
  1492.  
  1493.                             for(;;) {
  1494.                                 while(*s && isspace(*s))
  1495.                                     ++s;
  1496.  
  1497.                                 if (!*s)
  1498.                                     break;
  1499.  
  1500.                                 s2 = s;
  1501.  
  1502.                                 if (isxdigit(*s2)) {
  1503.                                     c = strchr(hexdig, toupper((int)(unsigned char)*s2++))-hexdig;
  1504.                                     if (isxdigit(*s2))
  1505.                                         c = c*16 + (strchr(hexdig, toupper((int)(unsigned char)*s2++))-hexdig);
  1506.  
  1507.                                     *t++ = (char)c;
  1508.                                 }
  1509.  
  1510.                                 if (s == s2) {
  1511.                                     SendMessage(hwndEdit, EM_SETSEL, s-text, s-text);
  1512.                                     SetFocus(hwndEdit);
  1513.                                     MessageBeep(MB_ICONEXCLAMATION);
  1514.  
  1515.                                     delete[] text;
  1516.  
  1517.                                     return 0;
  1518.                                 }
  1519.  
  1520.                                 s = s2;
  1521.                             }
  1522.  
  1523.                             l = t - text;
  1524.                         } else {
  1525.                             l = strlen(text);
  1526.                         }
  1527.                     }
  1528.  
  1529.                     delete[] pcd->pszFindString;
  1530.                     pcd->pszFindString = text;
  1531.                     pcd->nFindLength = l;
  1532.  
  1533.                     pcd->Find();
  1534.                 }
  1535.             }
  1536.         }
  1537.         return TRUE;
  1538.  
  1539.     case WM_DESTROY:
  1540.         pcd->hwndFind = NULL;
  1541.         pcd->mdnFind.Remove();
  1542.         return TRUE;
  1543.     }
  1544.     return FALSE;
  1545. }
  1546.  
  1547. ///////////////////////////////////////////////////////////////////////////
  1548.  
  1549. static inline bool isValidFOURCC(unsigned long l) {
  1550.     return isprint(l>>24) && isprint((l>>16)&0xff) && isprint((l>>8)&0xff) && isprint(l&0xff);
  1551. }
  1552.  
  1553. static const struct RIFFChunkInfo {
  1554.     unsigned long id;
  1555.     const char *desc;
  1556. } g_RIFFChunks[]={
  1557.     ' IVA', "Audio/video interleave file",
  1558.     'XIVA', "AVI2 extension block",
  1559.     'EVAW', "Sound waveform",
  1560.     0,
  1561. }, g_LISTChunks[]={
  1562.     'lrdh', "AVI file header block",
  1563.     'lrts', "AVI stream header block",
  1564.     'lmdo', "AVI2 extended header block",
  1565.     'ivom', "AVI data block",
  1566.     0,
  1567. }, g_JustChunks[]={
  1568.     'KNUJ', "padding",
  1569.     'hiva', "AVI file header",
  1570.     'hrts', "AVI stream header",
  1571.     'frts', "AVI stream format",
  1572.     'drts', "AVI stream codec data",
  1573.     'xdni', "AVI2 hierarchical indexing data",
  1574.     'hlmd', "AVI2 extended header",
  1575.     '1xdi', "AVI legacy index",
  1576.     'mges', "VirtualDub next segment pointer",
  1577.     0
  1578. };
  1579.  
  1580. static const char *LookupRIFFChunk(const RIFFChunkInfo *tbl, unsigned long ckid) {
  1581.     while(tbl->id) {
  1582.         if (tbl->id == ckid)
  1583.             return tbl->desc;
  1584.         ++tbl;
  1585.     }
  1586.  
  1587.     return "unknown";
  1588. }
  1589.  
  1590. struct RIFFScanInfo {
  1591.     ProgressDialog& pd;
  1592.     int count[100];
  1593.     __int64 size[100];
  1594.  
  1595.     RIFFScanInfo(ProgressDialog &_pd) : pd(_pd) {}
  1596. };
  1597.  
  1598. void HexViewer::_RIFFScan(RIFFScanInfo &rsi, HWND hwndTV, HTREEITEM hti, __int64 pos, __int64 sizeleft) {
  1599.     char buf[128];
  1600.  
  1601.     while(sizeleft > 8) {
  1602.         const char *cl;
  1603.         struct {
  1604.             unsigned long ckid, size, listid;
  1605.         } chunk;
  1606.         bool bExpand = false;
  1607.  
  1608.         rsi.pd.advance((long)(pos >> 10));
  1609.         rsi.pd.check();
  1610.  
  1611.         // Try to read 12 bytes at the current position.
  1612.  
  1613.         int off = (unsigned long)pos & 15;
  1614.  
  1615.         cl = FillRowCache(pos - off);
  1616.  
  1617.         memcpy(&chunk, cl+off, off<4 ? 12 : 16-off);
  1618.  
  1619.         if (off > 4) {
  1620.             cl = FillRowCache(pos+16 - off);
  1621.             memcpy((char *)&chunk + (16-off), cl, off-4);
  1622.         }
  1623.  
  1624.         // quick validation tests
  1625.  
  1626.         if (chunk.ckid == 'TSIL' || chunk.ckid == 'FFIR') {        // RIFF or LIST
  1627.             char *dst = buf+sprintf(buf, "%08I64X [%-4.4s:%-4.4s:%8ld]: ", pos, &chunk.ckid, &chunk.listid, chunk.size);
  1628.  
  1629.             if (sizeleft < 12 || chunk.size < 4 || chunk.size > sizeleft-8 || !isValidFOURCC(chunk.listid)) {
  1630.                 strcpy(dst, "invalid LIST/RIFF chunk");
  1631.                 sizeleft = 0;
  1632.             } else {
  1633.                 strcpy(dst, LookupRIFFChunk(chunk.ckid=='TSIL' ? g_LISTChunks : g_RIFFChunks, chunk.listid));
  1634.                 bExpand = true;
  1635.             }
  1636.         } else {
  1637.             char *dst = buf+sprintf(buf, "%08I64X [%-4.4s:%8ld]: ", pos, &chunk.ckid, chunk.size);
  1638.  
  1639.             if (!isValidFOURCC(chunk.ckid) || chunk.size > sizeleft-8) {
  1640.                 strcpy(dst, "invalid chunk");
  1641.                 sizeleft = 0;
  1642.             } else if (isdigit(chunk.ckid&0xff) && isdigit((chunk.ckid>>8)&0xff)) {
  1643.                 int stream = 10*(chunk.ckid&15) + ((chunk.ckid&0x0f00)>>8);
  1644.  
  1645.                 sprintf(dst, "stream %d: byte pos %8I64d, chunk %ld", stream, rsi.size[stream], rsi.count[stream]);
  1646.                 rsi.size[stream] += chunk.size;
  1647.                 rsi.count[stream] ++;
  1648.             } else
  1649.                 strcpy(dst, LookupRIFFChunk(g_JustChunks, chunk.ckid));
  1650.         }
  1651.  
  1652.         TVINSERTSTRUCT tvis;
  1653.  
  1654.         tvis.hParent        = hti;
  1655.         tvis.hInsertAfter    = TVI_LAST;
  1656.         tvis.item.mask        = TVIF_TEXT | TVIF_PARAM | TVIF_STATE;
  1657.         tvis.item.lParam    = 1;
  1658.         tvis.item.pszText    = buf;
  1659.         tvis.item.state        = TVIS_EXPANDED;
  1660.         tvis.item.stateMask    = TVIS_EXPANDED;
  1661.  
  1662.         HTREEITEM htiNew = TreeView_InsertItem(hwndTV, &tvis);
  1663.  
  1664.         if (bExpand)
  1665.             _RIFFScan(rsi, hwndTV, htiNew, pos+12, chunk.size-4);
  1666.  
  1667.         chunk.size = (chunk.size+1)&~1;
  1668.  
  1669.         pos += chunk.size + 8;
  1670.         sizeleft -= chunk.size + 8;
  1671.     }
  1672. }
  1673.  
  1674. void HexViewer::RIFFTree(HWND hwndTV) throw() {
  1675.     ProgressDialog pd(hwndTree, "Constructing RIFF tree", "Scanning file", (long)((i64FileSize+1023)>>10), true);
  1676.     RIFFScanInfo rsi(pd);
  1677.  
  1678.     pd.setValueFormat("%dK of %dK");
  1679.  
  1680.     memset(&rsi.count, 0, sizeof rsi.count);
  1681.     memset(&rsi.size, 0, sizeof rsi.size);
  1682.  
  1683.     try {
  1684.         _RIFFScan(rsi, hwndTV, TVI_ROOT, 0, i64FileSize);
  1685.     } catch(MyUserAbortError) {
  1686.     }
  1687.  
  1688.     SendMessage(hwndTV, WM_SETFONT, (WPARAM)hfont, TRUE);
  1689. }
  1690.  
  1691. BOOL APIENTRY HexViewer::TreeDlgProc(HWND hdlg, UINT msg, WPARAM wParam, LPARAM lParam) throw() {
  1692.     HexViewer *pcd = (HexViewer *)GetWindowLong(hdlg, DWL_USER);
  1693.  
  1694.     switch(msg) {
  1695.     case WM_INITDIALOG:
  1696.         SetWindowLong(hdlg, DWL_USER, lParam);
  1697.         pcd = (HexViewer *)lParam;
  1698.         pcd->hwndTree = hdlg;
  1699.         pcd->RIFFTree(GetDlgItem(hdlg, IDC_TREE));
  1700.         return TRUE;
  1701.  
  1702.     case WM_SIZE:
  1703.         SetWindowPos(GetDlgItem(hdlg, IDC_TREE), NULL, 0, 0, LOWORD(lParam), HIWORD(lParam), SWP_NOZORDER|SWP_NOACTIVATE);
  1704.         return TRUE;
  1705.  
  1706.     case WM_COMMAND:
  1707.         switch(LOWORD(wParam)) {
  1708.         case IDOK:
  1709.         case IDCANCEL:
  1710.             DestroyWindow(hdlg);
  1711.             return TRUE;
  1712.         }
  1713.         break;
  1714.  
  1715.     case WM_DESTROY:
  1716.         pcd->hwndTree = NULL;
  1717.         break;
  1718.  
  1719.     case WM_NOTIFY:
  1720.         if (GetWindowLong(((NMHDR *)lParam)->hwndFrom, GWL_ID) == IDC_TREE) {
  1721.             const NMHDR *pnmh = (NMHDR *)lParam;
  1722.  
  1723.             if (pnmh->code == NM_DBLCLK || (pnmh->code == TVN_KEYDOWN && ((LPNMTVKEYDOWN)lParam)->wVKey == VK_RETURN)) {
  1724.                 HTREEITEM hti = TreeView_GetSelection(pnmh->hwndFrom);
  1725.  
  1726.                 if (hti) {
  1727.                     char buf[128];
  1728.                     TVITEM tvi;
  1729.                     __int64 seekpos;
  1730.  
  1731.                     tvi.mask        = TVIF_TEXT | TVIF_PARAM;
  1732.                     tvi.hItem        = hti;
  1733.                     tvi.pszText        = buf;
  1734.                     tvi.cchTextMax    = sizeof buf;
  1735.  
  1736.                     TreeView_GetItem(pnmh->hwndFrom, &tvi);
  1737.  
  1738.                     if (tvi.lParam && 1==sscanf(buf, "%I64x", &seekpos)) {
  1739.                         pcd->MoveCaretToByte(seekpos);
  1740.                         PostMessage(hdlg, WM_APP, 0, 0);
  1741.                     }
  1742.                 }
  1743.  
  1744.                 SetWindowLong(hdlg, DWL_MSGRESULT, 1);
  1745.             }
  1746.             return TRUE;
  1747.         }
  1748.         break;
  1749.  
  1750.     case WM_APP:
  1751.         SetForegroundWindow(pcd->hwnd);
  1752.         SetFocus(pcd->hwnd);
  1753.         return TRUE;
  1754.     }
  1755.  
  1756.     return FALSE;
  1757. }
  1758.  
  1759. ///////////////////////////////////////////////////////////////////////////
  1760.  
  1761. ATOM RegisterHexViewer() {
  1762.     WNDCLASS wc;
  1763.  
  1764.     wc.style        = 0;
  1765.     wc.lpfnWndProc    = HexViewer::HexViewerWndProc;
  1766.     wc.cbClsExtra    = 0;
  1767.     wc.cbWndExtra    = sizeof(HexViewer *);
  1768.     wc.hInstance    = g_hInst;
  1769.     wc.hIcon        = NULL;
  1770.     wc.hCursor        = LoadCursor(NULL, IDC_ARROW);
  1771.     wc.hbrBackground= NULL; //(HBRUSH)(COLOR_WINDOW+1);
  1772.     wc.lpszMenuName    = MAKEINTRESOURCE(IDR_HEXVIEWER_MENU);
  1773.     wc.lpszClassName= HEXVIEWERCLASS;
  1774.  
  1775.     return RegisterClass(&wc);
  1776.  
  1777. }
  1778. void HexView(HWND hwndParent) {
  1779.     CreateWindowEx(
  1780.         WS_EX_CLIENTEDGE,
  1781.         HEXVIEWERCLASS,
  1782.         "VirtualDub Hex Editor",
  1783.         WS_OVERLAPPEDWINDOW | WS_VISIBLE | WS_VSCROLL,
  1784.         CW_USEDEFAULT,
  1785.         CW_USEDEFAULT,
  1786.         CW_USEDEFAULT,
  1787.         CW_USEDEFAULT,
  1788.         hwndParent,
  1789.         NULL,
  1790.         g_hInst,
  1791.         NULL);
  1792. }
  1793.